%{
#include "flexdef.h"
#include "parse.h"

#define ACTION_ECHO fprintf( temp_action_file, "%s", yytext )
#define MARK_END_OF_PROLOG fprintf( temp_action_file, "%%%% end of prolog\n" );

#undef YY_DECL
#define YY_DECL \
       int flexscan()

#define RETURNCHAR \
       yylval = yytext[0]; \
       return ( CHAR );

#define RETURNNAME \
       (void) strcpy( nmstr, yytext ); \
       return ( NAME );

#define PUT_BACK_STRING(str, start) \
       for ( i = strlen( str ) - 1; i >= start; --i ) \
           unput(str[i])
%}

%x SECT2 SECT2PROLOG SECT3 CODEBLOCK PICKUPDEF SC CARETISBOL NUM QUOTE
%x FIRSTCCL CCL ACTION RECOVER BRACEERROR C_COMMENT C_COMMENT_2 ACTION_COMMENT
%x ACTION_STRING PERCENT_BRACE_ACTION

WS              [ \t]+

OPTWS           [ \t]*

NAME            [a-z_][a-z_0-9]*

SCNAME          {NAME}

ESCSEQ          \\([^^\n]|"^".|0[0-9]{1,3})

%%
    static int bracelevel, didadef;
    int i, cclval;
    char nmdef[MAXLINE], myesc();

^{WS}.*\n              ++linenum; ECHO; /* indented code */
^#.*\n                 ++linenum; ECHO; /* treat as a comment */
^"/*"                  ECHO; BEGIN(C_COMMENT);
^"%s"(tart)?           return ( SCDECL );
^"%x"                  return ( XSCDECL );
^"%{".*\n              ++linenum; line_directive_out( stdout ); BEGIN(CODEBLOCK);
{WS}                   return ( WHITESPACE );

^"%%".*                        {
                       sectnum = 2;
                       line_directive_out( stdout );
                       BEGIN(SECT2PROLOG);
                       return ( SECTEND );
                       }

^"%"[^sx{%].*\n                {
                       fprintf( stderr,
                            "old-style lex command at line %d ignored:\n\t%s",
                                linenum, yytext );
                       ++linenum;
                       }

^{NAME}                        {
                       (void) strcpy( nmstr, yytext );
                       didadef = false;
                       BEGIN(PICKUPDEF);
                       }

{SCNAME}               RETURNNAME;
^{OPTWS}\n             ++linenum; /* allows blank lines in section 1 */
\n                     ++linenum; return ( '\n' );
.                      synerr( "illegal character" ); BEGIN(RECOVER);


<C_COMMENT>"*/"                ECHO; BEGIN(0);
<C_COMMENT>"*/".*\n    ++linenum; ECHO; BEGIN(0);
<C_COMMENT>[^*\n]+     ECHO;
<C_COMMENT>"*"         ECHO;
<C_COMMENT>\n          ++linenum; ECHO;

<CODEBLOCK>^"%}".*\n   ++linenum; BEGIN(0);
<CODEBLOCK>.*\n                ++linenum; ECHO;

<PICKUPDEF>{WS}                /* separates name and definition */

<PICKUPDEF>[^ \t\n].*  {
                       (void) strcpy( nmdef, yytext );

                       for ( i = strlen( nmdef ) - 1;
                             i >= 0 &&
                             nmdef[i] == ' ' || nmdef[i] == '\t';
                             --i )
                           ;

                       nmdef[i + 1] = '\0';

                        ndinstal( nmstr, nmdef );
                       didadef = true;
                       }

<PICKUPDEF>\n          {
                       if ( ! didadef )
                           synerr( "incomplete name definition" );
                       BEGIN(0);
                       ++linenum;
                       }

<RECOVER>.*\n          ++linenum; BEGIN(0); RETURNNAME;


<SECT2PROLOG>.*\n/[^ \t\n]     {
                       ++linenum;
                       ACTION_ECHO;
                       MARK_END_OF_PROLOG;
                       BEGIN(SECT2);
                       }

<SECT2PROLOG>.*\n      ++linenum; ACTION_ECHO;

<SECT2>^{OPTWS}\n      ++linenum; /* allow blank lines in section 2 */

       /* this horrible mess of a rule matches indented lines which
        * do not contain "/*".  We need to make the distinction because
        * otherwise this rule will be taken instead of the rule which
        * matches the beginning of comments like this one
        */
<SECT2>^{WS}([^/\n]|"/"[^*\n])*("/"?)\n        {
                       synerr( "indented code found outside of action" );
                       ++linenum;
                       }

<SECT2>"<"             BEGIN(SC); return ( '<' );
<SECT2>^"^"            return ( '^' );
<SECT2>\"              BEGIN(QUOTE); return ( '"' );
<SECT2>"{"/[0-9]               BEGIN(NUM); return ( '{' );
<SECT2>"{"[^0-9\n][^}\n]*      BEGIN(BRACEERROR);
<SECT2>"$"/[ \t\n]     return ( '$' );

<SECT2>{WS}"%{"                {
                       bracelevel = 1;
                       BEGIN(PERCENT_BRACE_ACTION);
                       return ( '\n' );
                       }
<SECT2>{WS}"|".*\n     ++linenum; return ( '\n' );

<SECT2>^{OPTWS}"/*"    ACTION_ECHO; BEGIN(C_COMMENT_2);

<SECT2>{WS}            { /* needs to be separate from following rule due to
                          * bug with trailing context
                          */
                       bracelevel = 0;
                       BEGIN(ACTION);
                       return ( '\n' );
                       }

<SECT2>{OPTWS}/\n      {
                       bracelevel = 0;
                       BEGIN(ACTION);
                       return ( '\n' );
                       }

<SECT2>^{OPTWS}\n      ++linenum; return ( '\n' );

<SECT2>^"%%".*         {
                       /* guarantee that the SECT3 rule will have something
                        * to match
                        */
                       yyless(1);
                       sectnum = 3;
                       BEGIN(SECT3);
                       return ( EOF ); /* to stop the parser */
                       }

<SECT2>"["([^\\\]\n]|{ESCSEQ})+"]"     {
                       (void) strcpy( nmstr, yytext );

                       /* check to see if we've already encountered this ccl */
                       if ( (cclval = ccllookup( nmstr )) )
                           {
                           yylval = cclval;
                           ++cclreuse;
                           return ( PREVCCL );
                           }
                       else
                           {
                           /* we fudge a bit.  We know that this ccl will
                            * soon be numbered as lastccl + 1 by cclinit
                            */
                           cclinstal( nmstr, lastccl + 1 );

                           /* push back everything but the leading bracket
                            * so the ccl can be rescanned
                            */
                           PUT_BACK_STRING(nmstr, 1);

                           BEGIN(FIRSTCCL);
                           return ( '[' );
                           }
                       }

<SECT2>"{"{NAME}"}"    {
                       register char *nmdefptr;
                       char *ndlookup();

                       (void) strcpy( nmstr, yytext );
                       nmstr[yyleng - 1] = '\0';  /* chop trailing brace */

                       /* lookup from "nmstr + 1" to chop leading brace */
                       if ( ! (nmdefptr = ndlookup( nmstr + 1 )) )
                           synerr( "undefined {name}" );

                       else
                           { /* push back name surrounded by ()'s */
                           unput(')');
                           PUT_BACK_STRING(nmdefptr, 0);
                           unput('(');
                           }
                       }

<SECT2>[/|*+?.()]      return ( yytext[0] );
<SECT2>.               RETURNCHAR;
<SECT2>\n              ++linenum; return ( '\n' );


<SC>","                        return ( ',' );
<SC>">"                        BEGIN(SECT2); return ( '>' );
<SC>">"/"^"            BEGIN(CARETISBOL); return ( '>' );
<SC>{SCNAME}           RETURNNAME;
<SC>.                  synerr( "bad start condition name" );

<CARETISBOL>"^"                BEGIN(SECT2); return ( '^' );


<QUOTE>[^"\n]          RETURNCHAR;
<QUOTE>\"              BEGIN(SECT2); return ( '"' );

<QUOTE>\n              {
                       synerr( "missing quote" );
                       BEGIN(SECT2);
                       ++linenum;
                       return ( '"' );
                       }


<FIRSTCCL>"^"/[^-\n]   BEGIN(CCL); return ( '^' );
<FIRSTCCL>"^"/-                return ( '^' );
<FIRSTCCL>-            BEGIN(CCL); yylval = '-'; return ( CHAR );
<FIRSTCCL>.            BEGIN(CCL); RETURNCHAR;

<CCL>-/[^\]\n]         return ( '-' );
<CCL>[^\]\n]           RETURNCHAR;
<CCL>"]"                       BEGIN(SECT2); return ( ']' );


<NUM>[0-9]+            {
                       yylval = myctoi( yytext );
                       return ( NUMBER );
                       }

<NUM>","                       return ( ',' );
<NUM>"}"                       BEGIN(SECT2); return ( '}' );

<NUM>.                 {
                       synerr( "bad character inside {}'s" );
                       BEGIN(SECT2);
                       return ( '}' );
                       }

<NUM>\n                        {
                       synerr( "missing }" );
                       BEGIN(SECT2);
                       ++linenum;
                       return ( '}' );
                       }


<BRACEERROR>"}"                synerr( "bad name in {}'s" ); BEGIN(SECT2);
<BRACEERROR>\n         synerr( "missing }" ); ++linenum; BEGIN(SECT2);


<PERCENT_BRACE_ACTION>{OPTWS}"%}".*    bracelevel = 0;
<PERCENT_BRACE_ACTION>.*               ACTION_ECHO;
<PERCENT_BRACE_ACTION>\n               {
                       ++linenum;
                       ACTION_ECHO;
                       if ( bracelevel == 0 )
                           {
                           fputs( "\tYY_BREAK\n", temp_action_file );
                           BEGIN(SECT2);
                           }
                       }

<ACTION>"{"            ACTION_ECHO; ++bracelevel;
<ACTION>"}"            ACTION_ECHO; --bracelevel;
<ACTION>[^{}"'/\n]+    ACTION_ECHO;
<ACTION>"/*"           ACTION_ECHO; BEGIN(ACTION_COMMENT);
<ACTION>"'"([^'\\\n]|\\.)*"'"  ACTION_ECHO; /* character constant */
<ACTION>\"             ACTION_ECHO; BEGIN(ACTION_STRING);
<ACTION>\n             {
                       ++linenum;
                       ACTION_ECHO;
                       if ( bracelevel == 0 )
                           {
                           fputs( "\tYY_BREAK\n", temp_action_file );
                           BEGIN(SECT2);
                           }
                       }
<ACTION>.              ACTION_ECHO;

<ACTION_COMMENT>"*/"   ACTION_ECHO; BEGIN(ACTION);
<ACTION_COMMENT>[^*\n]+        ACTION_ECHO;
<ACTION_COMMENT>"*"    ACTION_ECHO;
<ACTION_COMMENT>\n     ++linenum; ACTION_ECHO;
<ACTION_COMMENT>.      ACTION_ECHO;

<C_COMMENT_2>"*/"      ACTION_ECHO; BEGIN(SECT2);
<C_COMMENT_2>"*/".*\n  ++linenum; ACTION_ECHO; BEGIN(SECT2);
<C_COMMENT_2>[^*\n]+   ACTION_ECHO;
<C_COMMENT_2>"*"       ACTION_ECHO;
<C_COMMENT_2>\n                ++linenum; ACTION_ECHO;

<ACTION_STRING>[^"\\\n]+       ACTION_ECHO;
<ACTION_STRING>\\.     ACTION_ECHO;
<ACTION_STRING>\n      ++linenum; ACTION_ECHO;
<ACTION_STRING>\"      ACTION_ECHO; BEGIN(ACTION);
<ACTION_STRING>.       ACTION_ECHO;


<SECT2,QUOTE,CCL>{ESCSEQ}      {
                       yylval = myesc( yytext );
                       return ( CHAR );
                       }

<FIRSTCCL>{ESCSEQ}     {
                       yylval = myesc( yytext );
                       BEGIN(CCL);
                       return ( CHAR );
                       }


<SECT3>.|\n            {
                       register int numchars;

                       /* black magic - we know the names of a flex scanner's
                        * internal variables.  We cap the input buffer with
                        * an end-of-string and dump it to the output.
                        */
                       YY_DO_BEFORE_SCAN; /* recover from setting up yytext */

#ifdef FLEX_FAST_SKEL
                       fputs( yy_c_buf_p + 1, stdout );
#else
                       yy_ch_buf[yy_e_buf_p + 1] = '\0';

                       /* ignore the first character; it's the second '%'
                        * put back by the yyless(1) above
                        */
                       fputs( yy_ch_buf + yy_c_buf_p + 1, stdout );
#endif

                       /* if we don't do this, the data written by write()
                        * can get overwritten when stdout is finally flushed
                        */
                       (void) fflush( stdout );

                       while ( (numchars = read( fileno(yyin), yy_ch_buf,
                                                 YY_BUF_MAX )) > 0 )
                           (void) write( fileno(stdout), yy_ch_buf, numchars );
       
                       if ( numchars < 0 )
                           flexerror( "fatal read error in section 3" );

                       return ( EOF );
                       }
%%
